package com.demo.zk.server;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 分布式客户端
 * 客户端，是访问zk，得到所有服务器节点
 * @author Jerry.Zeng
 * @version v1.0.0
 * @date 2021/9/22 15:37
 */
public class DistributeClient {

    /**
     * 连接客户单
     */
    private static final String connectString = "localhost:2181";

    /**
     * 超时时间
     */
    private static final int sessionTimeout = 2000;

    private final String parentNode = "/servers";
    /**
     * 客户端
     */
    private ZooKeeper zk = null;

    public static void main(String[] args) throws Exception {
        DistributeClient client = new DistributeClient();
        // 1 获取zk连接 - 创建连接的时候进行监听
        client.getConnect();
        // 2 获取servers的子节点信息，从中获取服务器信息列表
        client.getServerList();
        // 3 业务进程启动
        client.business();
    }

    /**
     * 创建到zk的客户端连接
     * @throws IOException
     */
    public void getConnect() throws IOException {
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            /**
             * 创建监听器，假如连接断开，就执行查询其他节点列表
             * @param event
             */
            @Override
            public void process(WatchedEvent event) {
                // 由于监听只有一次，因此设置类似循环，再次启动监听
                try {
                    getServerList();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 获取服务器列表信息
     * @throws Exception
     */
    public void getServerList() throws Exception {
        /*
            1 获取服务器子节点信息，并且对父节点进行监听
            如果设置true - 表示走创建连接的时候，设置的监听
            如果new watch()表示走自己新建的监听器
         */
        List<String> children = zk.getChildren(parentNode, true);
        // 2 存储服务器信息列表
        ArrayList<String> servers = new ArrayList<>();
        // 3 遍历所有节点，获取节点中的主机名称信息
        for (String child : children) {
            // 取出所有节点的值，保存的主机信息
            byte[] data = zk.getData(
                    // 获取节点的路径
                    parentNode + "/" + child,
                    // 不需要监听
                    false,
                    // 状态
                    null
            );
            servers.add(new String(data));
        }
        // 4打印服务器列表信息
        System.out.println(servers);
    }

    /**
     * 业务功能
     * @throws Exception
     */
    public void business() throws Exception {
        System.out.println("client is working ...");
        Thread.sleep(Long.MAX_VALUE);
    }

}

